Or press ESC to close.

Decorator

Dec 6 2021 4 min read

Decorator: decorator is fastest way to wrap a function. this can also give us power to add extra functionality to wrapping function. we can decorator

                            
                               def decor(funcPointer):  # Decorator definition
                                def internalFunc(*args,**kwds):
                                    print("before")
                                    i = funcPointer(*args,**kwds)
                                    print("after")
                                    return i
                                return internalFunc
                            
                            @decor #wrapping is done by using "@" symbol
                            def sample(a,b,c):
                                print(a,b,c)

                            
                        

#here sample is wrapped into decorator and will be a internal function of the decorator function as in above example.

#all the arguments passed will be packed in *args and **kwds. and passed to internal function and mapped to the "sample" function arguments

                            
                                sample(1,2,3)   #calling the function
 
 
                                #output
                                before
                                1 2 3
                                after
                            
                        

#here 1 2 3 will be packed into *args in decor function and will be unpacked in the internal function resulting with a = 1, b = 2, c = 3. this will work as any normal function call.

# we can add extra code before and after calling the "funcPointer". which gives us additional functionality.

Example: if we want to have a timer for the sample function. we can start the timer before calling "funcPointer" and end it after. similarly, we do logging also.

                            
                                sample(b = 2,c=3,a =1)   #calling the function
 
                                #output
                                before
                                1 2 3
                                after

                            
                        

#here all args will captured by the **kwds and unpacked to internal function which results in positional arguments for internal function. assignment will be done like a = 1, b = 2, c = 3

Assignment: Write a decorator that can clock any function - More than 50% assignment turn around this time Yay !

                            
                                def mydecorator(func): # takes in a function pointer
                                    def innerfunc(*args, **kwds):
                                        import time
                                        start = time.time()
                                        i = func(*args, **kwds)   # this is remembered
                                        end = time.time()
                                        print(b-a, " seconds")
                                        return i # this matches with the return of actual function sent in.
                                    return innerfunc # this return statement is for mydecorator function.
                                # so far, with above code, what we have accomplished is that, wrapped the function that was passed.
                                 
                                def normalFunc():
                                    print("normalFunc is called")
                                 
                                print(normalFunc) # this is shown as , just a function
                                 
                                def dynamicFunc(): # going to create a dynamic decorator - note, '@mydecorator' just before this function definition
                                    print('dynamicFunc is called')
                                 
                                # below statement is how to dynamically apply a decorator in live session
                                decoratedDynamicFunc = mydecorator(dynamicFunc) # here , we will have decorated version of the function in decoratedDynamicFunc variable and non decorated version in dynamicFunc - you could verify by printing the mentioned variables
                                 
                                print(dynamicFunc) # this is shown as , just a function
                                print(decoratedDynamicFunc) # this is shown as a function that is internal to mydecorator
                                 
                                @mydecorator  # this is equivalent to decoratedFunc = mydecorator(decoratedFunc)
                                def decoratedFunc():
                                    print('decoratedFunc is called')
                                print(decoratedFunc) # this is shown as a function that is internal to mydecorator - Not sure, if there is a way to get the actual function from the decorated version of the function
                                
                            
                        

Example: Cascaded decorator

                            
                                def decor1(funcPointer):  # 1st Decorator definition
                                    def internalFunc(*args,**kwds):
                                        print("before 1st decorator")
                                        i = funcPointer(*args,**kwds)
                                        print("after 1st decorator")
                                        return i
                                    return internalFunc
                                
                                def decor2(funcPointer):  # 2nd Decorator definition
                                    def internalFunc(*args,**kwds):
                                        print("before 2nd decorator")
                                        i = funcPointer(*args,**kwds)
                                        print("after 2nd decorator")
                                        return i
                                    return internalFunc
                                
                                
                                @decor1 #decorating two times
                                @decor2
                                def sample(a,b,c):
                                    print(a,b,c)
                                
                                sample(1,2,3) # this will give output as below by following the call order
                                
                                #OUTPUT:
                                #before 1st decorator
                                #before 2nd decorator
                                #1 2 3
                                #after 2nd decorator
                                #after 1st decorator

                            
                        

Example: Time log of a function using Decorator

                            
                                def timeLogDecorator(func): # takes in a function pointer
                                    def innerfunc(*args, **kwds):
                                        import time
                                        start = time.time() # start time
                                        i = func(*args, **kwds)   # this is remembered
                                        end = time.time() # end time
                                        file = open(r"d:/temp/log.txt", "a") # opening file
                                        data = "Time taken by the '" + func.__name__ + "' is " + str(round(end-start, 4)) + " seconds\n" # data
                                        file.write(data) # write data
                                        file.close() # close
                                        return i # this matches with the return of actual function sent in.
                                    return innerfunc # this return statement is for mydecorator function.
                                # so far, with above code, what we have accomplished is that, wrapped the function that was passed and taking the time log of it.
                                
                                @timeLogDecorator
                                def normalFunc():
                                    print("normalFunc is called")
                                
                                normalFunc()  # this will log the time taken by the normal func in given file location
                                # this will give write as "Time taken by the 'normalFunc' is 0.004 seconds" in log file